<?php

namespace Think;

class Model {

// 属性
// 当前数据库操作对象
protected $db     =   null;
// 数据库对象池
private   $_db	 	=	  array();

// model对象池
private 	$_model = array();
// 当前存储的model序号
protected $akey		= 'a';
// relation 关系
private		$_relation = array();
private		$relation_model = array();
// 存储 根ModelSingle
private		$model_a	= null;

// 最近错误信息
protected $error    = '';
// 数据信息
protected $data     = array();
// 查询表达式参数
protected $options  = array();
protected $_map     = array();  // 字段映射定义 van: 废弃
protected $_scope   = array();  // 命名范围定义
// 链操作方法列表
protected $methods  = array('strict','order','having','group','lock','distinct','auto','filter','validate','result','token','index','force');


public function __construct() {
	// 载入relations表
	include MODULE_PATH.'/Model/relations.php';
	$this->relation_model = $_relations;

	// 数据库初始化操作 获取数据库操作对象 当前模型有独立的数据库连接信息
	// 无字段检测
	$this->db(0,empty($this->connection)?$connection:$this->connection,true);
}

// initRelation() 初始化 防止干扰
public function initRelation() {
	$this->akey	= 'a';
	$this->_relation = array();
}


/* db($linkNum='',$config='',$force=false) 切换当前的数据库连接
 * @access public
 * @param integer $linkNum  连接序号
 * @param mixed $config  数据库连接信息
 * @param boolean $force 强制重新连接
 * @return Model
 */
public function db($linkNum='',$config='',$force=false) {
	// db(0, '', true);
	if('' === $linkNum && $this->db) {
		return $this->db;
	}

	// 创建一个新的实例
	if(!isset($this->_db[$linkNum]) || $force ) {
		if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持读取配置参数
			$config = C($config);
		}
		$this->_db[$linkNum] = Db::getInstance($config);
	// 关闭数据库连接
	}elseif(NULL === $config){
		$this->_db[$linkNum]->close();
		unset($this->_db[$linkNum]);
		return ;
	}
	// 切换数据库连接
	$this->db = $this->_db[$linkNum];
	$this->_after_db();		// 空

	return $this;
}
// _after_db() * 数据库切换后回调方法
protected function _after_db() {}

/* model($name='', $tablePrefix='', $nameR_as=0, $field='*',$fieldExcept=false, $joinType='LEFT')
 * @return Model
 */
public function model($name='', $tablePrefix='', $nameR_as=0, $field,$fieldExcept=false, $joinType='LEFT') {
	// 获取模型名称 处理
	if(empty($name)) {
		// 不支持name为空
		E('model()中, 不允许参数$name为空 [ van:model() ]');
		return;
	}

	if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
		list($dbName,$name) = explode('.',$name);
	}else {
		$dbName = '';
	}
	$name = ucfirst($name);

	$db = $dbName?:C('DB_NAME');
	$tableTruePrefix = $this->_getTablePrefix($tablePrefix);
	$modelKey = strtolower($db.'.'.$tableTruePrefix.$name);

	if( !isset($this->_model[$modelKey]) ){
		$this->_model[$modelKey] = new ModelSingle($name, $tableTruePrefix, $dbName, $this->db);
	}
	$thisModel = $this->_model[$modelKey];

	if( 0 === $nameR_as ){
		$this->initRelation();		// 初始化 防止干扰
		$this->_relation['a'] = array($modelKey, $nameR_as);
		$this->model_a = $thisModel;

		$this->options['alias'] = 'a';

	}else if( is_string($nameR_as) ) {
		$this->akey = chr(ord($this->akey)+1);
		$this->_relation[$this->akey] = array($modelKey, $nameR_as);

		$modelR = $this->_model[$this->_relation[$nameR_as][0]];
		if( empty($modelR) ){
			E('$nameR_as 没有对应的 $modelR [ van:model() ]');
			return;
		}

		$prefix = $modelR->getTablePrefix();
		if( $prefix == C('DB_PREFIX') ){
			// 默认的prefix
			$nameR = $modelR->getModelName();
		}else {
			$nameR = $prefix.$modelR->getModelName();
		}

		if( '' != $tablePrefix ){
			$name = $tablePrefix.'_'.$name;
		}
		$relations = $this->relation_model[$nameR][$name];
		if( empty($relations) ){
			E( '缺少relations: '.$nameR.'=>'.$name.' [ van:model() ]');
		}


		// 处理 joins
		$rStr = '';
		if( is_array($relations['foreign_key']) ){
			// 复合键联结
			foreach($relations['foreign_key'] as $ky=>$vo){
				$rStr .= $nameR_as.'.'.$relations['foreign_key'][$ky].'='.$this->akey.'.'.$relations['parent_key'][$ky] . ' AND ';
			}

			$rStr = substr($rStr, 0, -5);
		}else {
			$rStr .= $nameR_as.'.'.$relations['foreign_key'].'='.$this->akey.'.'.$relations['parent_key'];
		}

		$this->options['join'][] = $joinType.' JOIN ' . $thisModel->getTableName() . ' AS ' . $this->akey . ' ON ' . $rStr;

	}else {
		E('$nameR_as 只能为0 或 单字母 [ van:model() ]');
	}


	// 处理field
	if( isset($field) ){
		$this->field($thisModel, $field,$fieldExcept);
	}

	return $this;

}
// _getTablePrefix($tablePrefix) * 获取表前缀 model()
private function _getTablePrefix($tablePrefix) {
	if(is_null($tablePrefix)) { 
		// 前缀为Null表示没有前缀
		$tablePrefix = '';
	}elseif('' != $tablePrefix) {
		$tablePrefix = $tablePrefix.'_';
	}else{
		$tablePrefix = C('DB_PREFIX');
	}

	return $tablePrefix;
}
/*///////////////以上为初始化///////////////////*/


/*///////////////以下为CURD/////////////////////*/
/* __set($name,$value) 设置数据对象的值
 * @access public
 * @param string $name 名称
 * @param mixed $value 值
 * @return void
 */
public function __set($name,$value) {
		// 设置数据对象属性
		$this->data[$name]  =   $value;
}
/* __get($name) 获取数据对象的值
 * @access public
 * @param string $name 名称
 * @return mixed
 */
public function __get($name) {
		return isset($this->data[$name])?$this->data[$name]:null;
}
/* __isset($name) 检测数据对象的值
 * @access public
 * @param string $name 名称
 * @return boolean
 */
public function __isset($name) {
		return isset($this->data[$name]);
}
/* __unset($name) 销毁数据对象的值
 * @access public
 * @param string $name 名称
 * @return void
 */
public function __unset($name) {
		unset($this->data[$name]);
}
/* __call($method,$args) 利用__call方法实现一些特殊的Model方法
 * @access public
 * @param string $method 方法名称
 * @param array $args 调用参数
 * @return mixed
 */
public function __call($method,$args) {
		if(in_array(strtolower($method),$this->methods,true)) {
				// 连贯操作的实现
				$this->options[strtolower($method)] =  $args[0];
				return $this;

		}elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){
				// 统计查询的实现
				$field =  isset($args[0])?$args[0]:'*';
				return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method);

		}elseif(strtolower(substr($method,0,5))=='getby') {
				// 根据某个字段获取记录
				$field   =   parse_name(substr($method,5));
				$where[$field] =  $args[0];
				return $this->where($where)->find();

		}elseif(strtolower(substr($method,0,10))=='getfieldby') {
				// 根据某个字段获取记录的某个值
				$name   =   parse_name(substr($method,10));
				$where[$name] =$args[0];
				return $this->where($where)->getField($args[1]);

		}elseif(isset($this->_scope[$method])){// 命名范围的单独调用支持
				return $this->scope($method,$args[0]);

		}else{
				E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
				return;
		}
}


// save
/* add($data='',$options=array(),$replace=false) 新增数据
 * @access public
 * @param mixed $data 数据
 * @param array $options 表达式
 * @param boolean $replace 是否replace
 * @return mixed
 */
public function add($data='',$options=array(),$replace=false) {
	if(empty($data)) {
		// 没有传递数据，获取当前数据对象的值
		if(!empty($this->data)) {
			$data           =   $this->data;
			$this->data     = array(); 			// 重置数据
		}else{
			$this->error    = L('_DATA_TYPE_INVALID_');
			return false;
		}
	}

	// 数据处理
	$data    = $this->_facade($data);
	// 分析表达式
	$options = $this->_parseOptions($options);
	if(false === $this->_before_insert($data,$options)) {
		return false;
	}

	// 写入数据到数据库
	$result = $this->db->insert($data,$options,$replace);
	if(false !== $result && is_numeric($result)) {
		$pk = $this->model_a->getPk();

		// 增加复合主键支持
		if(is_array($pk)) return $result;

		$insertId = $this->getLastInsID();
		if($insertId) {
			// 自增主键返回插入ID
			$data[$pk]  = $insertId;
			if(false === $this->_after_insert($data,$options)) {
				return false;
			}
			return $insertId;
		}
		if(false === $this->_after_insert($data,$options)) {
			return false;
		}
	}
	return $result;
}
// addAll($dataList,$options=array(),$replace=false)
public function addAll($dataList,$options=array(),$replace=false){
	if(empty($dataList)) {
		$this->error = L('_DATA_TYPE_INVALID_');
		return false;
	}

	// 数据处理
	foreach($dataList as $key=>$data){
		$dataList[$key] = $this->_facade($data);
	}

	// 分析表达式
	$options =  $this->_parseOptions($options);

	// 写入数据到数据库
	$result = $this->db->insertAll($dataList,$options,$replace);
	if(false !== $result ) {
		$insertId   =   $this->getLastInsID();
		if($insertId) {
			return $insertId;
		}
	}
	return $result;
}
/* selectAdd($fields='',$table='',$options=array()) 通过Select方式添加记录
 * @access public
 * @param string $fields 要插入的数据表字段名
 * @param string $table 要插入的数据表名
 * @param array $options 表达式
 * @return boolean
 */
public function selectAdd($fields='',$table='',$options=array()) {
	// 分析表达式
	$options =  $this->_parseOptions($options);

	// 写入数据到数据库
	if(false === $result = $this->db->selectInsert($fields?:$options['field'],$table?:$this->getTableName(),$options)){
		// 数据库插入操作失败
		$this->error = L('_OPERATION_WRONG_');
		return false;
	}else {
		// 插入成功
		return $result;
	}
}

/* getLastInsID() 返回最后插入的ID
 * @access public
 * @return string
 */
public function getLastInsID() {
	return $this->db->getLastInsID();
}
// _befroe_insert(&$data,$options) * 插入数据前的回调方法
protected function _before_insert(&$data,$options) {}
// _after_insert($data,$options) * 插入成功后的回调方法
protected function _after_insert($data,$options) {}

// update
/* save($data='',$options=array()) 保存数据
 * @access public
 * @param mixed $data 数据
 * @param array $options 表达式
 * @return boolean
 */
public function save($data='',$options=array()) {
	if(empty($data)) {
		// 没有传递数据，获取当前数据对象的值
		if(!empty($this->data)) {
			$data           =   $this->data;
			// 重置数据
			$this->data     =   array();
		}else{
			$this->error    =   L('_DATA_TYPE_INVALID_');
			return false;
		}
	}

	// 数据处理
	$data = $this->_facade($data);
	if(empty($data)){
		// 没有数据则不执行
		$this->error    =   L('_DATA_TYPE_INVALID_');
		return false;
	}

	// 分析表达式
	$options = $this->_parseOptions($options);

	$pk = $this->model_a->getPk();
	if(!isset($options['where']) ) {
		// 如果存在主键数据 则自动作为更新条件
		if (is_string($pk) && isset($data[$pk])) {
			$where[$pk]     =   $data[$pk];
			unset($data[$pk]);
		} elseif (is_array($pk)) {
			// 增加复合主键支持
			foreach ($pk as $field) {
				if(isset($data[$field])) {
					$where[$field]      =   $data[$field];
				} else {
					// 如果缺少复合主键数据则不执行
					$this->error        =   L('_OPERATION_WRONG_');
					return false;
				}
				unset($data[$field]);
			}
		}
		if(!isset($where)){
			// 如果没有任何更新条件则不执行
			$this->error        =   L('_OPERATION_WRONG_');
			return false;
		}else{
			$options['where']   =   $where;
		}
	}

	if(is_array($options['where']) && isset($options['where'][$pk])){
		$pkValue    =   $options['where'][$pk];
	}
	if(false === $this->_before_update($data,$options)) {
		return false;
	}
	$result = $this->db->update($data,$options);

	if(false !== $result && is_numeric($result)) {
		if(isset($pkValue)) $data[$pk]   =  $pkValue;
		$this->_after_update($data,$options);
	}
	return $result;
}
/* setField($field,$value='') 设置记录的某个字段值 借用save()完成
 * 支持使用数据库字段和方法
 * @access public
 * @param string|array $field  字段名
 * @param string $value  字段值
 * @return boolean
 */
public function setField($field,$value='') {
	if(is_array($field)) {
		$data           =   $field;
	}else{
		$data[$field]   =   $value;
	}
	return $this->save($data);
}
/* setInc($field,$step=1,$lazyTime=0) 字段值增长 借用setField()完成
 * @access public
 * @param string $field  字段名
 * @param integer $step  增长值
 * @param integer $lazyTime  延时时间(s)
 * @return boolean
 */
public function setInc($field,$step=1,$lazyTime=0) {
	if($lazyTime>0) {// 延迟写入
		$condition 	=  	$this->options['where'];
		$guid 		=  	md5($this->model_a->getModelName().'_'.$field.'_'.serialize($condition));
		$step 		= 	$this->lazyWrite($guid,$step,$lazyTime);
		if(empty($step)) {
			return true; // 等待下次写入
		}elseif($step < 0) {
			$step 	=	'-'.$step;
		}
	}
	return $this->setField($field,array('exp',$field.'+'.$step));
}
/* setDec($field,$step=1,$lazyTime=0) 字段值减少 借用setField()完成
 * @access public
 * @param string $field  字段名
 * @param integer $step  减少值
 * @param integer $lazyTime  延时时间(s)
 * @return boolean
 */
public function setDec($field,$step=1,$lazyTime=0) {
	if($lazyTime>0) {// 延迟写入
		$condition  =  	$this->options['where'];
		$guid 		=  	md5($this->model_a->getModelName().'_'.$field.'_'.serialize($condition));
		$step 		= 	$this->lazyWrite($guid,-$step,$lazyTime);
		if(empty($step)) {
			return true; // 等待下次写入
		}elseif($step > 0) {
			$step 	=	'-'.$step;
		}
	}
	return $this->setField($field,array('exp',$field.'-'.$step));
}

/* lazyWrite($guid,$step,$lazyTime) * 延时更新检查 返回false表示需要延时 setInc() setDec()
 * 否则返回实际写入的数值
 * @access public
 * @param string $guid  写入标识
 * @param integer $step  写入步进值
 * @param integer $lazyTime  延时时间(s)
 * @return false|integer
 */
protected function lazyWrite($guid,$step,$lazyTime) {
		if(false !== ($value = S($guid))) { // 存在缓存写入数据
				if(NOW_TIME > S($guid.'_time')+$lazyTime) {
						// 延时更新时间到了，删除缓存数据 并实际写入数据库
						S($guid,NULL);
						S($guid.'_time',NULL);
						return $value+$step;
				}else{
						// 追加数据到缓存
						S($guid,$value+$step);
						return false;
				}
		}else{ // 没有缓存数据
				S($guid,$step);
				// 计时开始
				S($guid.'_time',NOW_TIME);
				return false;
		}
}
// _before_update(&$data,$options) * 更新数据前的回调方法
protected function _before_update(&$data,$options) {}
// _after_update($data,$options) * 更新成功后的回调方法
protected function _after_update($data,$options) {}

/* _facdade($data) * 对保存到数据库的数据进行处理 add()addAll() save()
 * @access protected
 * @param mixed $data 要操作的数据
 * @return boolean
 */
protected function _facade($data) {
	// 2017-06-07 van:阻止 多model join 使用CUD, 仅在查询中使用  
	if( count($this->_relation) > 1 ){
		E('多model, 仅能在查询中使用 [ van:_facade() ]');
		exit;
	}

	// 检查数据字段合法性 2017-06-07 van:此时仅 a.field这一种了
	if( $this->options['field'] ) {
		// 使用 field() 手动字段过滤的
		$fields = $this->options['field']['a'];
		unset($this->options['field']);
	}else {
		// 使用 model自己的 字段过滤
		$fields = $this->model_a->getDbFields();
	}

	// 字段过滤
	foreach($data as $key=>$val) {
		$key = trim($key);

		if(!in_array($key,$fields,true)){
			if(!empty($this->options['strict'])){
				E(L('_DATA_TYPE_INVALID_').':['.$key.'=>'.$val.']');
			}
			unset($data[$key]);
		}else if(is_scalar($val)) {
			// 字段类型检查 和 强制转换 2017-06-07 van:此时仅 a.field这一种了
			$col = $key;		// van:以后完善
			$this->_parseType($data, 'a', $col, $key);
		}
	}

	// 安全过滤
	if(!empty($this->options['filter'])) {
		$data = array_map($this->options['filter'],$data);
		unset($this->options['filter']);
	}
	$this->_before_write($data);

	return $data;
}
// _before_write(&$data) * 写入数据前的回调方法 包括新增和更新 _facade()
protected function _before_write(&$data) {}

// read
/* select($options=array()) 查询数据集
 * @access public
 * @param array $options 表达式参数
 * @return mixed
 */
public function select($options=array()) {
	$pk = $this->model_a->getPk();
	if(is_string($options) || is_numeric($options)) {
		// 根据主键查询
		if(strpos($options,',')) {
			$where[$pk] = array('IN',$options);
		}else{
			$where[$pk] = $options;
		}
		$options          = array();
		$options['where'] = $where;
	}elseif (is_array($options) && (count($options) > 0) && is_array($pk)) {
		// 根据复合主键查询
		$count = 0;
		foreach (array_keys($options) as $key) {
			if(is_int($key)) $count++; 
		} 
		if($count == count($pk)) {
			$i = 0;
			foreach ($pk as $field) {
				$where[$field] = $options[$i];
				unset($options[$i++]);
			}
			$options['where'] = $where;
		} else {
			return false;
		}
	} elseif(false === $options){ // 用于子查询 不查询只返回SQL
		$options['fetch_sql'] = true;
	}

	// 分析表达式
	if( empty($this->options['field']) ){		// 仅在select() find()
		$this->options['field'] = array('a'=>array('*'));
	}else if( empty($this->options['field']['a']) ){
		$this->options['field'] = array_merge_recursive( array('a'=>array('*')), $this->options['field'] );
	}
	$options = $this->_parseOptions($options, true);

	// 判断查询缓存
	if(isset($options['cache'])){
		$cache = $options['cache'];
		$key   = is_string($cache['key'])?$cache['key']:md5(serialize($options));
		$data  = S($key,'',$cache);
		if(false !== $data){
			return $data;
		}
	}
	$resultSet = $this->db->select($options);
	if(false === $resultSet) {
			return false;
	}
	if(!empty($resultSet)) { // 有查询结果
		if(is_string($resultSet)){
			return $resultSet;
		}

		// $resultSet = array_map(array($this,'_read_data'),$resultSet);  // 字段映射 van: 废弃
		$this->_after_select($resultSet,$options);
		if(isset($options['index'])){ // 对数据集进行索引
			$index  =   explode(',',$options['index']);
			foreach ($resultSet as $result){
				$_key   =  $result[$index[0]];
				if(isset($index[1]) && isset($result[$index[1]])){
					$cols[$_key] =  $result[$index[1]];
				}else{
					$cols[$_key] =  $result;
				}
			}
			$resultSet = $cols;
		}
	}

	if(isset($cache)){
		S($key,$resultSet,$cache);
	}

	return $resultSet;
}
/* find($options=array()) 查询数据
 * @access public
 * @param mixed $options 表达式参数
 * @return mixed
 */
public function find($options=array()) {
	$pk = $this->model_a->getPk();
	if(is_numeric($options) || is_string($options)) {
		$where[$pk] = $options;
		$options          = array();
		$options['where'] = $where;
	}else if(is_array($options) && (count($options) > 0) && is_array($pk)) {
		// 根据复合主键查询
		$count = 0;
		foreach (array_keys($options) as $key) {
			if(is_int($key)) $count++; 
		} 
		if($count == count($pk)) {
			$i = 0;
			foreach ($pk as $field) {
				$where[$field] = $options[$i];
				unset($options[$i++]);
			}
			$options['where']  =  $where;
		} else {
			return false;
		}
	}
	// 总是查找一条记录
	$options['limit'] = 1;

	// 分析表达式
	if( empty($this->options['field']) ){		// 仅在select() find()
		$this->options['field'] = array('a'=>array('*'));
	}else if( empty($this->options['field']['a']) ){
		$this->options['field'] = array_merge_recursive( array('a'=>array('*')), $this->options['field'] );
	}
	$options = $this->_parseOptions($options, true);

	// 判断查询缓存
	if(isset($options['cache'])){
			$cache = $options['cache'];
			$key   = is_string($cache['key'])?$cache['key']:md5(serialize($options));
			$data  = S($key,'',$cache);
			if(false !== $data){
				$this->data = $data;
				return $data;
			}
	}
	$resultSet = $this->db->select($options);
	if(false === $resultSet) {
		return false;
	}
	if(empty($resultSet)) {// 查询结果为空
		return null;
	}
	if(is_string($resultSet)){
		return $resultSet;
	}

	// 读取数据后的处理
	// $data = $this->_read_data($resultSet[0]);		// 字段映射 van: 废弃
	$data = $resultSet[0];

	$this->_after_find($data,$options);
	$this->data = $data;
	if(isset($cache)){
		S($key,$data,$cache);
	}
	return $this->data;
}
/* getField($field,$sepa=null) van:获取一条记录的某个字段值
 * @access public
 * @param string $field  字段名
 * @param string $spea  字段数据间隔符号 NULL返回数组
 * @return mixed
 */
public function getField($field,$sepa=null) {
	$options['field'][$this->akey] = $field;

	// 分析表达式
	$options = $this->_parseOptions($options, true);

	// 判断查询缓存
	if(isset($options['cache'])){
		$cache = $options['cache'];
		$key   = is_string($cache['key'])?$cache['key']:md5($sepa.serialize($options));
		$data  = S($key,'',$cache);
		if(false !== $data){
			return $data;
		}
	}        
	$field = trim($field);
	if(strpos($field,',') && false !== $sepa) { // 多字段
		if(!isset($options['limit'])){
			$options['limit'] = is_numeric($sepa)?$sepa:'';
		}
		$resultSet          = $this->db->select($options);

		if(!empty($resultSet)) {
			if(is_string($resultSet)){
				return $resultSet;
			}            	

			$_field = explode(',', $field);
			$field  = array_keys($resultSet[0]);
			$key1   = array_shift($field);
			$key2   = array_shift($field);
			$cols   = array();
			$count  = count($_field);
			foreach($resultSet as $result) {
				$name = $result[$key1];
				if(2==$count) {
					$cols[$name] = $result[$key2];
				}else{
					$cols[$name] = is_string($sepa)?implode($sepa,array_slice($result,1)):$result;
				}
			}
			if(isset($cache)){
				S($key,$cols,$cache);
			}
			return $cols;
		}

	}else{   // 查找一条记录
		// 返回数据个数
		if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
			$options['limit'] = is_numeric($sepa)?$sepa:1;
		}
		$result = $this->db->select($options);
		if(!empty($result)) {
			if(is_string($result)){
				return $result;
			}            	
			if(true !== $sepa && 1==$options['limit']) {
				$data = reset($result[0]);
				if(isset($cache)){
					S($key,$data,$cache);
				}            
				return $data;
			}
			foreach ($result as $val){
				if( $fieldAt = strpos($field, '.') ){
					$field = substr($field, $fieldAt+1);
				}
				$array[] = $val[$field];
			}
			if(isset($cache)){
				S($key,$array,$cache);
			}                
			return $array;
		}
	}
	return null;
}

/* _read_data($data) * 字段映射 van:废弃 数据读取后的处理 select() find()
 * @access protected
 * @param array $data 当前数据
 * @return array
 */
protected function _read_data($data) {
	// 检查字段映射
	if(!empty($this->_map) && C('READ_DATA_MAP')) {
		foreach ($this->_map as $key=>$val){
			if(isset($data[$val])) {
				$data[$key] =   $data[$val];
				unset($data[$val]);
			}
		}
	}
	return $data;
}
// _after_select(&$resultSet,$options) * 查询成功后的回调方法
protected function _after_select(&$resultSet,$options) {}
// _after_find(&$result,$options) * 查询成功的回调方法
protected function _after_find(&$result,$options) {}
// array('count','sum','min','max','avg')
/* buildSql() 生成查询SQL 可用于子查询
 * @access public
 * @return string
 */
public function buildSql() {
	return  '( '.$this->fetchSql(true)->select().' )';
}
/* fetchSql($fetch=true) 获取执行的SQL语句 buildSql()
 * @access public
 * @param boolean $fetch 是否返回sql
 * @return Model
 */
public function fetchSql($fetch=true){
	$this->options['fetch_sql'] = $fetch;
	return $this;
}

// delete
/* delete($options=array()) 删除数据
 * @access public
 * @param mixed $options 表达式
 * @return mixed
 */
public function delete($options=array()) {
	$pk = $this->model_a->getPk();
	if(empty($options) && empty($this->options['where'])) {
		// 如果删除条件为空 则删除当前数据对象所对应的记录
		if(!empty($this->data) && isset($this->data[$pk]))
			return $this->delete($this->data[$pk]);
		else
			return false;
	}
	if(is_numeric($options)  || is_string($options)) {
		// 根据主键删除记录
		if(strpos($options,',')) {
			$where[$pk]     =  array('IN', $options);
		}else{
			$where[$pk]     =  $options;
		}
		$options          =  array();
		$options['where'] =  $where;
	}
	// 根据复合主键删除记录
	if (is_array($options) && (count($options) > 0) && is_array($pk)) {
		$count = 0;
		foreach (array_keys($options) as $key) {
			if (is_int($key)) $count++; 
		} 
		if($count == count($pk)) {
			$i = 0;
			foreach ($pk as $field) {
				$where[$field] = $options[$i];
				unset($options[$i++]);
			}
			$options['where']  =  $where;
		} else {
			return false;
		}
	}
	// 分析表达式
	$options = $this->_parseOptions($options);
	if(empty($options['where'])){
		// 如果条件为空 不进行删除操作 除非设置 1=1
		return false;
	}        
	if(is_array($options['where']) && isset($options['where'][$pk])){
		$pkValue = $options['where'][$pk];
	}

	if(false === $this->_before_delete($options)) {
		return false;
	}        
	$result = $this->db->delete($options);
	if(false !== $result && is_numeric($result)) {
		$data = array();
		if(isset($pkValue)) $data[$pk] = $pkValue;
		$this->_after_delete($data,$options);
	}
	// 返回删除记录个数
	return $result;
}
// _before_delete($options) * 删除数据前的回调方法
protected function _before_delete($options) {}    
// _after_delete($data,$options) * 删除成功后的回调方法
protected function _after_delete($data,$options) {}

// 数据分析
/* _parseOptions($options=array()) * van:分析表达式options CURD() parseSql()
 * @access protected
 * @param array $options 表达式参数
 * @return array
 */
protected function _parseOptions($options=array(), $isRead=false) {
	if(is_array($options)) {
		$options = array_merge($this->options,$options);
	}

	if(!isset($options['table'])){
		// 自动获取表名
		$options['table'] = $this->model_a->getTableName();
	}

	// 数据表别名
	if($isRead && !empty($options['alias'])) {
		$options['table'] .= ' AS '.$options['alias'];
	}
	// 记录操作的模型名称
	$options['model'] = $this->model_a->getModelName();

	// 解析field
	$fieldsArr = null;
	if( $options['field'] ){
		foreach($options['field'] as $akey=>$fields){
			if( is_string($fields) ){
				$fields = explode(',',$fields);
			}else {
				foreach($fields as &$field){
					if( false===strpos($field, '(') && false === strpos($field,'|') && false === strpos($field,'&') && !is_numeric($field) && '_' != substr($field,0,1)){ 	// 排除field()中的方法
						$field = $akey.'.'.$field;
					}
				}
			}
			if( empty($fieldsArr) ){
				$fieldsArr = $fields; 
			}else {
				$fieldsArr = array_merge($fieldsArr, $fields);
			}
		}
		$options['field'] = $fieldsArr;
		unset($field);

	}else if( $isRead ){
		// read是不能为空
		E("Model::read时不能为空, 可以加上'*' [ van:_parseOptions ]");
	}

	// 解析where字段类型验证 van重写 原本join()不验证
	if( isset($options['where']) && is_array($options['where']) ) {
		// 对数组查询条件进行字段类型检查
		foreach ($options['where'] as $key=>$val){
			if( !strpos($key,'.') ){
				// 无别名AS model_a
				$akey = 'a';
				$col = trim($key);
				$mkey = $this->_relation['a'][0];
			}else{
				list($akey, $col) = explode('.', $key);
				$mkey = $this->_relation[$akey][0];
			}
			$fields = $this->_model[$mkey]->getDbFields();

			if(in_array($col, $fields, true)){
				if(is_scalar($val)) {
					$this->_parseType($options['where'], $akey, $col, $key);
				}
			}elseif(!is_numeric($col) && '_' != substr($col,0,1) && false === strpos($col,'(') && false === strpos($col,'|') && false === strpos($col,'&')){
				if(!empty($this->options['strict'])){
					E(L('_ERROR_QUERY_EXPRESS_').':['.$col.'=>'.$val.']');
				}else{
					E("1054:Unknown column '".$col."' in 'where list' [ van:SQL语句 ]");
				}
			}
		}
	}
	// 查询过后清空sql表达式组装 避免影响下次查询
	$this->options = array();
	// 表达式过滤
	$this->_options_filter($options);

	return $options;
}
// _options_filter(&$options) * 表达式过滤回调方法 _parseOptions()
protected function _options_filter(&$options) {}
/* _parseType(&$data,$key) * van:字段类型检查和强制转换 _facade() _parseOptions()
 * @access protected
 * @param mixed $data 数据
 * @param string $col 字段名 不含 $akey
 * @param string $key 	data[$key] 可能为: $akey.col van:此处bind参数绑定 没有验证
 * @return void
 */
protected function _parseType(&$data, $akey, $col, $key) {
	$mkey = $this->_relation[$akey][0];
	$fieldType = $this->_model[$mkey]->getFields()['_type'][$col];

	if(!isset($this->options['bind'][':'.$key]) && isset($fieldType)){
		$fieldType = strtolower($fieldType);
		if(false !== strpos($fieldType,'enum')){
			// 支持ENUM类型优先检测

		}else if(false===strpos($fieldType,'bigint') && (false!==strpos($fieldType,'int') || false!==strpos($fieldType,'unsigned')) ) {
			$data[$key] = intval($data[$key]);

		}else if(false!==strpos($fieldType,'float') || false!==strpos($fieldType,'double') || false!=strpos($fieldType,'decimal')){
			$data[$key] = floatval($data[$key]);

		}else if(false!==strpos($fieldType,'bool')){
			$data[$key] = (bool)$data[$key];
		}
	}
}

/* query($sql,$parse=false) SQL查询
 * @access public
 * @param string $sql  SQL指令
 * @param mixed $parse  是否需要解析SQL
 * @return mixed
 */
public function query($sql,$parse=false) {
	if(!is_bool($parse) && !is_array($parse)) {
		$parse = func_get_args();
		array_shift($parse);
	}
	$sql  =   $this->parseSql($sql,$parse);
	return $this->db->query($sql);
}
/* execute($sql,$parse=false) * 执行SQL语句
 * @access public
 * @param string $sql  SQL指令
 * @param mixed $parse  是否需要解析SQL
 * @return false | integer
 */
public function execute($sql,$parse=false) {
	if(!is_bool($parse) && !is_array($parse)) {
		$parse = func_get_args();
		array_shift($parse);
	}
	$sql  =   $this->parseSql($sql,$parse);
	return $this->db->execute($sql);
}
/* parseSql($sql,$parse) * 解析SQL语句 query() execute()
 * @access public
 * @param string $sql  SQL指令
 * @param boolean $parse  是否需要解析SQL
 * @return string
 */
protected function parseSql($sql,$parse) {
	// 分析表达式
	if(true === $parse) {
		$options =  $this->_parseOptions();
		$sql    =   $this->db->parseSql($sql,$options);
	}elseif(is_array($parse)){ // SQL预处理
		$parse  =   array_map(array($this->db,'escapeString'),$parse);
		$sql    =   vsprintf($sql,$parse);
	}else{
		$sql    =   strtr( $sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>$this->model_a->getTablePrefix()) );
		$prefix =   $this->model_a->getTablePrefix();
		$sql    =   preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $sql);
	}

	$this->db->setModel($this->model_a->getModelName());
	return $sql;
}


// 连贯操作 
//__call(): array('strict','order','having','group','lock','distinct','auto','filter','validate','result','token','index','force');
/* field($field,$except=false) ** 指定查询字段 支持字段排除
 * @access public
 * @param mixed $field
 * @param boolean $except 是否排除
 * @return Model
 */
private function field($model, $field='*', $except=false){
	if(true === $field) {// 获取全部字段
		$fields = $model->getDbFields();		// 仅返回当前的Model	
		$field = $fields?:'*';
	}

	if( $field == '*' ){
		$field = array('*');
		//$field[] = $this->akey.'.*';
		
	}else{
		if(is_string($field)) {
			$field = explode(',',$field);
		}

		if( is_array($field) ){
			// 去除空白
			foreach($field as &$val){
				$val = trim($val);
			}

			if($except) {// 字段排除
				$fields = $model->getDbFields();
				$field = $fields? array_diff($fields,$field): $field;
			}
			/*
			foreach($field as &$val){
				if( false===strpos($val, '(') && false === strpos($val,'|') && false === strpos($val,'&') && !is_numeric($val) && '_' != substr($val,0,1)){ 	// 排除field()中的方法
					$val = $this->akey.'.'.$val;
				}
			}
			*/
		}
	}

	$fieldArr = array($this->akey => $field);
	if( empty($this->options['field']) ){
		$this->options['field'] = $fieldArr;
	}else{
		$this->options['field'] = array_merge($this->options['field'], $fieldArr);
	}
	return $this;
}
/* data($data='') 设置数据对象值
* @access public
* @param mixed $data 数据
* @return Model
*/
public function data($data=''){
	if('' === $data && !empty($this->data)) {
			return $this->data;
	}
	if(is_object($data)){
			$data   =   get_object_vars($data);
	}elseif(is_string($data)){
			parse_str($data,$data);
	}elseif(!is_array($data)){
			E(L('_DATA_TYPE_INVALID_'));
	}
	$this->data = $data;
	return $this;
}
/* table($table) van:仅用于子查询 取消__TABLE__
* 指定当前的数据表
* @access public
* @param mixed $table
* @return Model
*/
public function table($table) {
	if(is_array($table)) {
		$this->options['table'] = $table;
	}elseif(!empty($table)) {
		$this->options['table'] =   $table;
	}
	return $this;
}
/* using($using) USING支持 用于多表删除
* @access public
* @param mixed $using
* @return Model
*/
public function using($using){
	$prefix = $this->model_a->getTablePrefix();
	if(is_array($using)) {
			$this->options['using'] =   $using;
	}elseif(!empty($using)) {
			//将__TABLE_NAME__替换成带前缀的表名
			$using  = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $using);
			$this->options['using'] =   $using;
	}
	return $this;
}
/* join($join,$type='INNER') 查询SQL组装 join
* @access public
* @param mixed $join
* @param string $type JOIN类型
* @return Model
*/
public function join($join,$type='INNER') {
	$prefix = $this->model_a->getTablePrefix();
	if(is_array($join)) {
			foreach ($join as $key=>&$_join){
					$_join  =   preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $_join);
					$_join  =   false !== stripos($_join,'JOIN')? $_join : $type.' JOIN ' .$_join;
			}
			$this->options['join']      =   $join;
	}elseif(!empty($join)) {
			//将__TABLE_NAME__字符串替换成带前缀的表名
			$join  = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $join);
			$this->options['join'][]    =   false !== stripos($join,'JOIN')? $join : $type.' JOIN '.$join;
	}
	return $this;
}
/* union($union,$all=false) 查询SQL组装 union
 * @access public
 * @param mixed $union
 * @param boolean $all
 * @return Model
 */
public function union($union,$all=false) {
		if(empty($union)) return $this;
		if($all) {
				$this->options['union']['_all']  =   true;
		}
		if(is_object($union)) {
				$union   =  get_object_vars($union);
		}
		// 转换union表达式
		if(is_string($union) ) {
				$prefix = $this->model_a->getTablePrefix();
				//将__TABLE_NAME__字符串替换成带前缀的表名
				$options  = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $union);
		}elseif(is_array($union)){
				if(isset($union[0])) {
						$this->options['union']  =  array_merge($this->options['union'],$union);
						return $this;
				}else{
						$options =  $union;
				}
		}else{
				E(L('_DATA_TYPE_INVALID_'));
		}
		$this->options['union'][]  =   $options;
		return $this;
}
/* cache($key=true,$expire=null,$type='') 查询缓存
 * @access public
 * @param mixed $key
 * @param integer $expire
 * @param string $type
 * @return Model
 */
public function cache($key=true,$expire=null,$type=''){
		// 增加快捷调用方式 cache(10) 等同于 cache(true, 10)
		if(is_numeric($key) && is_null($expire)){
				$expire = $key;
				$key    = true;
		}
		if(false !== $key)
				$this->options['cache']  =  array('key'=>$key,'expire'=>$expire,'type'=>$type);
		return $this;
}
/* scope($scope='',$args=NULL) 调用命名范围
 * @access public
 * @param mixed $scope 命名范围名称 支持多个 和直接定义
 * @param array $args 参数
 * @return Model
 */
public function scope($scope='',$args=NULL){
		if('' === $scope) {
				if(isset($this->_scope['default'])) {
						// 默认的命名范围
						$options    =   $this->_scope['default'];
				}else{
						return $this;
				}
		}elseif(is_string($scope)){ // 支持多个命名范围调用 用逗号分割
				$scopes         =   explode(',',$scope);
				$options        =   array();
				foreach ($scopes as $name){
						if(!isset($this->_scope[$name])) continue;
						$options    =   array_merge($options,$this->_scope[$name]);
				}
				if(!empty($args) && is_array($args)) {
						$options    =   array_merge($options,$args);
				}
		}elseif(is_array($scope)){ // 直接传入命名范围定义
				$options        =   $scope;
		}
		
		if(is_array($options) && !empty($options)){
				$this->options  =   array_merge($this->options,array_change_key_case($options));
		}
		return $this;
}
/* where($where,$parse=null) 指定查询条件 支持安全过滤
 * @access public
 * @param mixed $where 条件表达式
 * @param mixed $parse 预处理参数
 * @return Model
 */
public function where($where,$parse=null){
		if(!is_null($parse) && is_string($where)) {
				if(!is_array($parse)) {
						$parse = func_get_args();
						array_shift($parse);
				}
				$parse = array_map(array($this->db,'escapeString'),$parse);
				$where = vsprintf($where,$parse);
		}elseif(is_object($where)){
				$where = get_object_vars($where);
		}
		if(is_string($where) && '' != $where){
				$map   = array();
				$map['_string'] = $where;
				$where = $map;
		}        
		if(isset($this->options['where'])){
				$this->options['where'] = array_merge($this->options['where'],$where);
		}else{
				$this->options['where'] = $where;
		}
		
		return $this;
}
/* limit($offset,$length=null) 指定查询数量
 * @access public
 * @param mixed $offset 起始位置
 * @param mixed $length 查询数量
 * @return Model
 */
public function limit($offset,$length=null){
		if(is_null($length) && strpos($offset,',')){
				list($offset,$length)   =   explode(',',$offset);
		}
		$this->options['limit']     =   intval($offset).( $length? ','.intval($length) : '' );
		return $this;
}
/* page($page,$listRows=null) 指定分页
 * @access public
 * @param mixed $page 页数
 * @param mixed $listRows 每页数量
 * @return Model
 */
public function page($page,$listRows=null){
		if(is_null($listRows) && strpos($page,',')){
				list($page,$listRows)   =   explode(',',$page);
		}
		$this->options['page']      =   array(intval($page),intval($listRows));
		return $this;
}
/* comment($comment) 查询注释
 * @access public
 * @param string $comment 注释
 * @return Model
 */
public function comment($comment){
		$this->options['comment'] =   $comment;
		return $this;
}
/* bind($key,$value=false) 参数绑定
 * @access public
 * @param string $key  参数名
 * @param mixed $value  绑定的变量及绑定参数
 * @return Model
 */
public function bind($key,$value=false) {
		if(is_array($key)){
				$this->options['bind'] =    $key;
		}else{
				$num =  func_num_args();
				if($num>2){
						$params =   func_get_args();
						array_shift($params);
						$this->options['bind'][$key] =  $params;
				}else{
						$this->options['bind'][$key] =  $value;
				}        
		}
		return $this;
}
/* alias($alias) 指定表别名, 参数为true则使用当前默认
 * @access public
 * @param string $alias
 * @return Model
 */
public function alias($alias) {
	if(true === $alias){
		$alias = $this->akey;
	}
	$this->options['alias'] = $alias;

	return $this;
}


// 数据表相关
/* getLastSql() 返回最后执行的sql语句
 * @access public
 * @return string
 */
public function getLastSql() {
	$mkey = $this->_relation[$this->akey][0];
	$thisModel = $this->_model[ $this->_relation[$this->akey][0] ];
	$modelName = $thisModel->getModelName();

	return $this->db->getLastSql($modelName);
}
// _sql() 鉴于getLastSql比较常用 增加_sql 别名
public function _sql(){
		return $this->getLastSql();
}
// _options() 返回options参数
public function _options() {
	return $this->options;
}

/* getError() 返回模型的错误信息
 * @access public
 * @return string
 */
public function getError(){
		return $this->error;
}
/* getDbError() 返回数据库的错误信息
 * @access public
 * @return string
 */
public function getDbError() {
		return $this->db->getError();
}


// 验证
/* vValidate($value, $rule, $type='regex')
 * van自定义手动 验证数据-数据库表层 支持 unique confirm
 * @access public
 * @param string $value 验证数据
 * @param mixed $rule 验证表达式
 * @param string $type 验证方式 默认为正则验证
 * @return boolean
 */
public function vValidate($value, $rule, $type='regex'){
	$type = strtolower(trim($type));
	$rule = strtolower(trim($rule));

	if( $type == 'unique' ){
		if( !strpos($rule,'.') ){
			// 无别名AS model_a
			$akey = 'a';
			$key = trim($key);
			$mkey = $this->_relation['a'][0];
		}else{
			list($akey, $key) = explode('.', $key);
			$mkey = $this->_relation[$akey][0];
		}

		if( !in_array($rule, $this->_model[$mkey]->getDbfields()) ){
			$this->error = '验证规则的字段名不正确!';
			return false;
		}else{
			$map[$rule] = $value;
			if($this->where($map)->find())   return false;
			return true;
		}

	}else {
			return $this->check($value, $rule, $type);
	}
}
/* check($value,$rule,$type='regex'){
 * 验证数据 支持 in between equal length regex expire ip_allow ip_deny
 * @access public
 * @param string $value 验证数据
 * @param mixed $rule 验证表达式
 * @param string $type 验证方式 默认为正则验证
 * @return boolean
 */
public function check($value,$rule,$type='regex'){
		$type = strtolower(trim($type));
		switch($type) {
				case 'in': // 验证是否在某个指定范围之内 逗号分隔字符串或者数组
				case 'notin':
						$range   = is_array($rule)? $rule : explode(',',$rule);
						return $type == 'in' ? in_array($value ,$range) : !in_array($value ,$range);
				case 'between': // 验证是否在某个范围
				case 'notbetween': // 验证是否不在某个范围            
						if (is_array($rule)){
								$min    =    $rule[0];
								$max    =    $rule[1];
						}else{
								list($min,$max)   =  explode(',',$rule);
						}
						return $type == 'between' ? $value>=$min && $value<=$max : $value<$min || $value>$max;
				case 'equal': // 验证是否等于某个值
				case 'notequal': // 验证是否等于某个值            
						return $type == 'equal' ? $value == $rule : $value != $rule;
				case 'length': // 验证长度
						$length  =  mb_strlen($value,'utf-8'); // 当前数据长度
						if(strpos($rule,',')) { // 长度区间
								list($min,$max)   =  explode(',',$rule);
								return $length >= $min && $length <= $max;
						}else{// 指定长度
								return $length == $rule;
						}
				case 'expire':
						list($start,$end)   =  explode(',',$rule);
						if(!is_numeric($start)) $start   =  strtotime($start);
						if(!is_numeric($end)) $end   =  strtotime($end);
						return NOW_TIME >= $start && NOW_TIME <= $end;
				case 'ip_allow': // IP 操作许可验证
						return in_array(get_client_ip(),explode(',',$rule));
				case 'ip_deny': // IP 操作禁止验证
						return !in_array(get_client_ip(),explode(',',$rule));
				case 'regex':
				default:    // 默认使用正则验证 可以使用验证类中定义的验证名称
						// 检查附加规则
						return $this->regex($value,$rule);
		}
}
/* regex($value,$rule) 使用正则验证数据 chech()
 * @access public
 * @param string $value  要验证的数据
 * @param string $rule 验证规则
 * @return boolean
 */
public function regex($value,$rule) {
		$validate = array(
				'require'   =>  '/\S+/',
				'tel'   		=>  '/^0?1[34578][0-9]{9}$/',
				'email'     =>  '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/',
				'url'       =>  '/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(:\d+)?(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/',
				'currency'  =>  '/^\d+(\.\d+)?$/',
				'number'    =>  '/^\d+$/',
				'zip'       =>  '/^\d{6}$/',
				'integer'   =>  '/^[-\+]?\d+$/',
				'double'    =>  '/^[-\+]?\d+(\.\d+)?$/',
				'english'   =>  '/^[A-Za-z]+$/',
		);
		// 检查是否有内置的正则表达式
		if(isset($validate[strtolower($rule)]))
				$rule       =   $validate[strtolower($rule)];
		return preg_match($rule,$value)===1;
}


// 事务
/* startTrans() 启动事务
 * @access public
 * @return void
 */
public function startTrans() {
		$this->commit();
		$this->db->startTrans();
		return ;
}
/* commit() 提交事务
 * @access public
 * @return boolean
 */
public function commit() {
		return $this->db->commit();
}
/* rollback()* 事务回滚
 * @access public
 * @return boolean
 */
public function rollback() {
		return $this->db->rollback();
}


// 其他
/* setProperty($name,$value) 设置模型的属性值 model的set方法
 * @access public
 * @param string $name 名称
 * @param mixed $value 值
 * @return Model
 */
public function setProperty($name,$value) {
		if(property_exists($this,$name))
				$this->$name = $value;
		return $this;
}
/* procedure($sql, $parse = false) 存储过程返回多数据集
 * @access public
 * @param string $sql  SQL指令
 * @param mixed $parse  是否需要解析SQL
 * @return array
 */
public function procedure($sql, $parse = false) {
		return $this->db->procedure($sql, $parse);
}


}
